package dominion.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import dominion.model.Card;
import dominion.model.Deck;
import dominion.model.GameBoard;
import dominion.model.Player;
import dominion.model.actions.Action;
import dominion.model.monies.Coin;
import dominion.model.points.Estate;
import dominion.utils.Constants;
import dominion.view.Printer;

public class GameState {

	private GameBoard board;
	private List<Player> players;
	private Printer printer;

	public GameState() {
		board = new GameBoard();
		players = new ArrayList<Player>();
		printer = new Printer();
	}

	public void initGame(List<Card> kingdomCards, List<Player> players) {
		int victoryCards = 0;
		int curseCards = 0;
		if (players.size() == 2)
		{
			victoryCards = 8;
			curseCards = 10;
		}
		else if (players.size() == 3)
		{
			victoryCards = 12;
			curseCards = 20;
		}
		else if (players.size() == 4)
		{
			victoryCards = 12;
			curseCards = 30;
		}
		else
		{
			printer.print("ERROR: Invalid player number in initGame");
			System.exit(1);
		}

		board.createNewBoardGame(kingdomCards, victoryCards, curseCards);
		for (Player current : players)
		{
			List<Card> start = new ArrayList<Card>();
			start.addAll(board.drawFromPile(Constants.COPPER_PILE, 7));
			for (int k = 0; k < 3; k++)
			{
				start.add(new Estate());
			}
			current.addToDeck(start, Constants.LIBRARY_DECK);
			current.shuffleDeck(Constants.LIBRARY_DECK);
			current.drawHand();
			this.players.add(current);
		}

		printer.print("Starting board status:");
		printer.printBoard(board);
	}

	public void startGame() {
		boolean victory = false;
		while (!victory)
		{
			for (Player current : players)
			{
				current.setNewTurn();
				printer.playerStatus(current);
				printer.printOptions();
				BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
				try
				{
					String answer = null;
					boolean next = false;
					boolean buyPhase = false;
					while (!next)
					{
						if (isGameOver())
						{
							victory = true;
							break;
						}
						answer = in.readLine().trim();
						if (answer.equals(Constants.BOARD_STATUS))
						{
							printer.printBoard(board);
							printer.printOptions();
						}
						else if (answer.equals(Constants.PLAYER_STATUS))
						{
							printer.playerStatus(current);
							printer.printOptions();
						}
						else if (answer.equals(Constants.END_TURN))
						{
							endTurn(current);
							next = true;
						}
						else if (answer.startsWith("help"))
						{
							String split = answer.substring(4).trim().toLowerCase();
							Card card = Constants.cardMap.get(split);
							if (card == null)
							{
								printer.print("Invalid answer");
							}
							else
							{
								printer.printCardHelp(card.toString());
							}
						}
						else if (answer.startsWith("buy"))
						{
							String split = answer.substring(3).trim().toLowerCase();
							Card card = Constants.cardMap.get(split);
							if (card == null)
							{
								printer.print("Invalid answer");
							}
							else
							{
								buyCard(card.toString(), current);
								buyPhase = true;
							}
						}
						else if (answer.equalsIgnoreCase("all"))
						{
							buyPhase = true;
							playAllMoney(current);
						}
						else if (!current.getHand().contains(answer))
						{
							printer.print("Card not in hand.");
						}
						else
						{
							Card card = Constants.cardMap.get(answer.toLowerCase());
							if (card instanceof Action && buyPhase)
							{
								printer.print("Can not play action cards in the buy phase.");
							}
							else
							{
								processCard(card, current);
							}
							if (card instanceof Coin)
							{
								buyPhase = true;
							}
						}
					}
					victory = isGameOver();
				}
				catch (IOException e)
				{
					e.printStackTrace();
				}
			}
		}
		printer.print("GAME IS OVER\n");
		gameOver();
	}

	private void processCard(Card card, Player player) throws IOException {
		if (player.getActions() > 0 || !(card instanceof Action))
		{
			card.doAction(player, board, players);
			printer.print(card.toString() + " was played.");
		}
		else
		{
			printer.print("Not enough actions");
		}
		printer.printOptions();
	}

	private void buyCard(String cardName, Player player) {
		Card card = Constants.cardMap.get(cardName.toLowerCase());
		if (card == null)
		{
			printer.print(cardName.toLowerCase() + " is not available\n");
		}
		else
		{
			Deck pile = board.getMap().get(cardName.toLowerCase());
			if (pile == null || pile.isEmpty())
			{
				printer.print(cardName.toLowerCase() + " is not available\n");
			}
			else if (player.getMoney() < card.getCost())
			{
				printer.print("Not enough money. " + card.toString() + " cost " + card.getCost()
						+ " monies and you only have " + player.getMoney() + "\n");
			}
			else if (player.getBuys() < 1)
			{
				printer.print("You don't have any buys remaining\n");
			}
			else
			{
				player.addToDeck(board.drawFromPile(cardName), Constants.DISCARD_DECK);
				player.changeMoney(-1 * card.getCost());
				player.changeBuys(-1);
				printer.print("You bought a " + card.toString() + "\n");
			}
		}
		printer.printOptions();
	}

	private void playAllMoney(Player player) throws IOException {
		List<Card> monies = new ArrayList<Card>();
		for (Card card : player.getHand().getList())
		{
			if (card instanceof Coin)
			{
				monies.add(card);
			}
		}
		for (Card card : monies)
		{
			processCard(card, player);
		}
	}

	private void endTurn(Player player) {
//		Deck hand = player.getHand();
//		while (!hand.isEmpty())
//		{
//			player.addToDeck(hand.drawTopCard(), Constants.DISCARD_DECK);
//		}
		player.endTurn();
		player.drawHand();
	}

	private boolean isGameOver() {
		boolean result = false;
		if (board.getMap().get("province").isEmpty())
		{
			result = true;
		}
		int emptyPile = 0;
		for (String key : board.getMap().keySet())
		{
			if (board.getMap().get(key).isEmpty())
			{
				emptyPile++;
			}
		}
		if (emptyPile >= 3)
		{
			result = true;
		}
		return result;
	}

	private void gameOver() {
		List<String> winners = new ArrayList<String>();
		int highScore = 0;
		printer.print("Final victory point totals:");
		for (Player current : players)
		{
			int points = current.getVictoryPoints();
			printer.print("Player: " + current.getName());
			printer.print("Points: " + points);
			printer.print("Hand: ");
			printer.print(current.printHand());
			printer.print("Discard: ");
			printer.print(current.printDiscard());
			printer.print("Lib: ");
			printer.print(current.printLib());
			printer.print("Play Space: ");
			printer.print(current.printPlaySpace());
			if (points == highScore)
			{
				winners.add(current.getName());
			}
			else if (points > highScore)
			{
				highScore = points;
				winners.clear();
				winners.add(current.getName());
			}
		}
		printer.printWinners(winners);
	}
}
